<?php

/**
 * CCoreMimeEmail
 * CCoreMimeEmailCollection
 *
 * @author Usenko Timur <usenko.timur@gmail.com>
 * @package mime
 */


CCore::Using('base.collection');
CCore::Using('base.utils');
CCore::Using('mime.constants');

/**
 * @package mime
 */
class CCoreMimeEmail
{
	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @var string
	 */
	private $_sDisplayName;

	/**
	 * @var string
	 */
	private $_sEmail;

	/**
	 * @var string
	 */
	private $_sRemarks;
	#</editor-fold>

	/**
	 * @param string $sEmail = ''
	 * @param string $sDisplayName = ''
	 * @param string $sRemarks = ''
	 * @return CCoreMimeEmail
	 */
	public function __construct($sEmail = '', $sDisplayName = '', $sRemarks = '')
	{
		CCore::CheckTypes(array('$sEmail', '$sDisplayName', '$sRemarks'));

		$this->_sEmail = $sEmail;
		$this->_sDisplayName = $sDisplayName;
		$this->_sRemarks = $sRemarks;
	}

	/**
	 * @return string
	 */
	public function Email()
	{
		return $this->_sEmail;
	}

	/**
	 * @return string
	 */
	public function DisplayName()
	{
		return $this->_sDisplayName;
	}

	/**
	 * @return string
	 */
	public function Remarks()
	{
		return $this->_sRemarks;
	}

	/**
	 * @return string
	 */
	public function GetAccountName()
	{
		return CCoreBaseUtils::GetAccountNameFromEmail($this->_sEmail);
	}

	/**
	 * @return string
	 */
	public function GetDomain()
	{
		return CCoreBaseUtils::GetDomainFromEmail($this->_sEmail);
	}

	/**
	 * @return string
	 */
	public function ToString()
	{
		$sReturn = '';
		$sRemarks = (empty($this->_sRemarks)) ? '' : '('.str_replace(')', '\)', $this->_sRemarks).')';
		$sDisplayName = (empty($this->_sDisplayName)) ? '' : '"'.str_replace('"', '\"', $this->_sDisplayName).'"';

		if (!empty($this->_sEmail))
		{
			$sReturn = $this->_sEmail;
		}

		if (strlen($sDisplayName.$sRemarks) > 0)
		{
			$sReturn = (strlen($sReturn) > 0)
				? $sDisplayName.' <'.$sReturn.'> '.$sRemarks : $sDisplayName.' '.$sRemarks;
		}

		return trim($sReturn);
	}

	/**
	 * @return void
	 */
	public function Reset()
	{
		$this->_sEmail = '';
		$this->_sDisplayName = '';
		$this->_sRemarks = '';
	}

	/**
	 * @param string $sEmailAddress
	 * @return bool
	 */
	public function Parse($sEmailAddress)
	{
		CCore::CheckTypes(array('$sEmailAddress'));

		$this->Reset();
		
		if (empty($sEmailAddress))
		{
			return false;
		}

		$sEmailAddress = trim($sEmailAddress);

		$sName = '';
		$sEmail = '';
		$sComment = '';

		$bInName = false;
		$bInAddress = false;
		$bInComment = false;

		$iStartIndex = 0;
		$iEndIndex = 0;
		$iCurrentIndex = 0;

		while ($iCurrentIndex < strlen($sEmailAddress))
		{
			switch ($sEmailAddress{$iCurrentIndex})
			{
				case '"':
					if ((!$bInName) && (!$bInAddress) && (!$bInComment))
					{
						$bInName = true;
						$iStartIndex = $iCurrentIndex;
					}
					elseif ((!$bInAddress) && (!$bInComment))
					{
						$iEndIndex = $iCurrentIndex;
						$sName = substr($sEmailAddress, $iStartIndex + 1, $iEndIndex - $iStartIndex - 1);
						$sName = strtr($sName, array('\\\\' => '\\', '\"' => '"'));
						$sEmailAddress = substr_replace($sEmailAddress, '', $iStartIndex, $iEndIndex - $iStartIndex + 1);
						$iEndIndex = 0;
						$iCurrentIndex = 0;
						$iStartIndex = 0;
						$bInName = false;
					}
					break;
				case '<':
					if ((!$bInName) && (!$bInAddress) && (!$bInComment))
					{
						if ($iCurrentIndex > 0 && strlen($sName) == 0)
						{
							$sName = substr($sEmailAddress, 0, $iCurrentIndex - 1);
						}

						$bInAddress = true;
						$iStartIndex = $iCurrentIndex;
					}
					break;
				case '>':
					if ($bInAddress)
					{
						$iEndIndex = $iCurrentIndex;
						$sEmail = substr($sEmailAddress, $iStartIndex + 1, $iEndIndex - $iStartIndex - 1);
						$sEmailAddress = substr_replace($sEmailAddress, '', $iStartIndex, $iEndIndex - $iStartIndex + 1);
						$iEndIndex = 0;
						$iCurrentIndex = 0;
						$iStartIndex = 0;
						$bInAddress = false;
					}
					break;
				case '(':
					if ((!$bInName) && (!$bInAddress) && (!$bInComment))
					{
						$bInComment = true;
						$iStartIndex = $iCurrentIndex;
					}
					break;
				case ')':
					if ($bInComment)
					{
						$iEndIndex = $iCurrentIndex;
						$sComment = substr($sEmailAddress, $iStartIndex + 1, $iEndIndex - $iStartIndex - 1);
						$sEmailAddress = substr_replace($sEmailAddress, '', $iStartIndex, $iEndIndex - $iStartIndex + 1);
						$iEndIndex = 0;
						$iCurrentIndex = 0;
						$iStartIndex = 0;
						$bInComment = false;
					}
					break;
				case '\\':
					$iCurrentIndex++;
					break;
			}

			$iCurrentIndex++;
		}

		if (strlen($sEmail) == 0)
		{
			$aRegs = array('');
			if (preg_match('/[^@\s]+@\S+/i', $sEmailAddress, $aRegs) && isset($aRegs[0]))
			{
				$sEmail = $aRegs[0];
			}
			else
			{
				$sName = $sEmailAddress;
			}
		}

		if ((strlen($sEmail) > 0) && (strlen($sName) == 0) && (strlen($sComment) == 0))
		{
			$sName = str_replace($sEmail, '', $sEmailAddress);
		}

		$this->_sEmail = trim(trim($sEmail), '<>');
		$this->_sDisplayName = trim(trim($sName),'"');
		$this->_sRemarks = trim(trim($sComment),'()');
		
		return (0 < strlen($this->_sEmail));
	}
}

/**
 * @package mime
 */
class CCoreMimeEmailCollection extends ACoreBaseCollection
{
	/**
	 * @param string $nsRawEmails = null
	 * @return CCoreMimeEmails
	 */
	public function __construct($nsRawEmails = null)
	{
		CCore::CheckTypes(array('$nsRawEmails'));

		parent::__construct();
		
		if (null !== $nsRawEmails)
		{
			$this->_parse($nsRawEmails);
		}
	}

	/**
	 * @param CCoreMimeEmail $oMimeEmail
	 * @return void
	 */
	public function Add(CCoreMimeEmail $oMimeEmail)
	{
		CCore::CheckTypes(array('$oMimeEmail'));

		parent::Add($oMimeEmail);
	}

	/**
	 * @return string
	 */
	public function ToString()
	{
		$aReturn = array();
		$aEmails =& $this->GetAsArray();
		foreach($aEmails as /* @var $oEmail CCoreMimeEmail */ $oEmail)
		{
			$aReturn[] = $oEmail->ToString();
		}
		return implode(', ', $aReturn);
	}

	/**
	 * @param	string	$sRawEmails
	 * @return	bool
	 */
	private function _parse($sRawEmails)
	{
		CCore::CheckTypes(array('$sRawEmails'));

		$this->Clear();
		
		if (empty($sRawEmails))
		{
			return false;
		}

		$sWorkingRecipients = trim($sRawEmails);

		$iEmailStartPos = 0;
		$iEmailEndPos = 0;

		$bIsInQuotes = false;
		$sChQuote = '"';
		$bIsInAngleBrackets = false;
		$bIsInBrackets = false;

		$iCurrentPos = 0;

		$sWorkingRecipientsLen = strlen($sWorkingRecipients);

		while ($iCurrentPos < $sWorkingRecipientsLen)
		{
			switch ($sWorkingRecipients{$iCurrentPos})
			{
				case '\'':
				case '"':
					if (!$bIsInQuotes)
					{
						$sChQuote = $sWorkingRecipients{$iCurrentPos};
						$bIsInQuotes = true;
					}
					elseif ($sChQuote == $sWorkingRecipients{$iCurrentPos})
					{
						$bIsInQuotes = false;
					}
					break;

				case '<':
					if (!$bIsInAngleBrackets)
					{
						$bIsInAngleBrackets = true;
					}
					break;

				case '>':
					if ($bIsInAngleBrackets)
					{
						$bIsInAngleBrackets = false;
					}
					break;

				case '(':
					if (!$bIsInBrackets)
					{
						$bIsInBrackets = true;
					}
					break;

				case ')':
					if ($bIsInBrackets)
					{
						$bIsInBrackets = false;
					}
					break;

				case ',':
				case ';':
					if (!$bIsInAngleBrackets && !$bIsInBrackets && !$bIsInQuotes)
					{
						$iEmailEndPos = $iCurrentPos;
						$oEmailAddr = new CCoreMimeEmail();
						$oEmailAddr->Parse(substr($sWorkingRecipients, $iEmailStartPos, $iEmailEndPos - $iEmailStartPos));
						$this->Add($oEmailAddr);
						$iEmailStartPos = $iCurrentPos + 1;
						unset($oEmailAddr);
					}
					break;
			}

			$iCurrentPos++;
		}

		if ($iEmailStartPos < $iCurrentPos)
		{
			$oEmailAddr = new CCoreMimeEmail();
			$oEmailAddr->Parse(substr($sWorkingRecipients, $iEmailStartPos, $iCurrentPos - $iEmailStartPos));
			$this->Add($oEmailAddr);
			unset($oEmailAddr);
		}

		return true;
	}
}